import { Socket } from 'socket.io';
import events from 'events';
import { makeLabelLogger } from './util/logger';
import { Agent } from './models/Agent';
import { DH_UNABLE_TO_CHECK_GENERATOR } from 'constants';
import { Contact } from 'wechaty';
import { net as netAPI } from './protocolConst.json';
import { sleep } from './util/tools';

let { llog } = makeLabelLogger('Cli');
export class ActionArgs {
	client: Client;
	action: any;
	processed: boolean;
	send(res) {
		// this.client.emit('action', res);
	}
}
class AgentSub {
	agent: Agent;
	listeners: Object;
}

export class Client {
	protected static allClients = new Map<string, Client>();
	public static actionEmitter = new events.EventEmitter();
	public static createClient(sock) {
		let client = new Client(sock);
		this.allClients[client.id] = client;
		return client;
	}
	//////////////////////////////////////////////////////////////////////////
	protected socket: Socket;
	protected allAgents = new Map<string, AgentSub>();

	constructor(sock: Socket) {
		this.socket = sock;
		this.init();
	}

	init() {
		llog('**client connected:', this.socket.request.connection.remoteAddress, this.socket.id);
		this.socket.on('error', error => llog('**client error', error));
		this.socket.on('disconnect', () => this.onDisconnect());

		this.socket.on('action', action => {
			try {
				llog('get action', action.type, action.payload);
				Client.actionEmitter.emit(action.type, this, action);
			} catch (error) {
				llog('emit action error', error);
			}
		});
	}
	get id() {
		return this.socket.id;
	}

	onDisconnect() {
		llog('**client disconnected', this.socket.request.connection.remoteAddress);
		this.allAgents.forEach((v: AgentSub) => {
			console.log('unsub agent', v.agent.id);
			this.unsubscribeAgent(v.agent);
		});
	}
	sendAction(action) {
		this.socket.emit('action', action);
	}

	unsubscribeAgent(agent: Agent) {
		let sub: AgentSub = this.allAgents.get(agent.id);
		if (!sub) return;
		Object.keys(sub.listeners).forEach(k => {
			agent.bot.removeListener(k, sub.listeners[k]);
		});
		agent.subCount--;
		this.allAgents.delete(agent.id);
	}
	subscribeAgent(agent: Agent, userdata) {
		// if (this.allAgents.has(agent.id)) return;
		//
		let listeners = {
			scan: qrCode => {
				agent.scanCount++;
				llog('onscan', userdata, agent.scanCount, qrCode, this.id);
				this.sendAction({
					type: netAPI.onScan,
					payload: { qrCode, id: agent.id, userdata, scanCount: agent.scanCount },
				});
			},
			login: async (user: Contact) => {
				llog('onlogin', user.name());
				let avatar = await user.avatar();
				let avatarString = await avatar.toBase64();
				this.sendAction({
					type: netAPI.onLogin,
					payload: { id: agent.id, name: user.name(), avatar: avatarString },
				});
				// user.say('bot已登录:' + new Date().toLocaleTimeString());
				await sleep(1000);
			},
			logout: (user: Contact) => {
				llog('onlogout', user.name());
				this.sendAction({ type: netAPI.onLogout, payload: { id: agent.id, name: user.name() } });
			},
			stop: () => {
				llog('on stop');
				this.sendAction({ type: netAPI.onLogout, payload: { id: agent.id } });
			},
			error: err => {
				llog('onerror', err);
				this.sendAction({ type: netAPI.onError, payload: { id: agent.id, err } });
			},
		};
		for (let k in listeners) {
			agent.bot.addListener(k, listeners[k]);
		}

		let sub = new AgentSub();
		sub.agent = agent;
		sub.listeners = listeners;
		this.allAgents.set(agent.id, sub);
		agent.subCount++;

		// check agent is already connected?
		if (agent.wxid) {
			listeners.login(agent.self);
		} else if (agent.qrCode) {
			listeners.scan(agent.qrCode);
		}
		llog('subagent', this.id, agent.id, agent.runState);
		agent.start();
	}
}
